# 修改纪要：
# 1. 加入避让作业逻辑，向正面吊与轨道吊类中加入：（1）避让作业事件（2）避让作业进程（3）避让位置属性 (完成)
# 2. 向调度作业管理器中加入等待、避让调度机制，依据作业优先级进行调度
# 3. 修改正面吊的移动逻辑，跨线移动时，需要先移动到堆场最右侧，再移动到另外一条作业线，再移动到目标位置
# 4. 每次移动完成都要进行一次调度
# 5. 差速超越问题与安全距离问题已解决
# 模型结构:
# 正面吊模块<-调度作业<-作业调度控制器模块->调度作业->轨道吊模块
# 调度节点：每次设备移动完成后进行一次调度（事件驱动）， 即对轨道吊与正面吊的动作命令进行下发
# （三种情况：对设备下发新任务、无操作、中断设备当前的移动动作进行等待或避让、恢复移动作业）
# 设备动作：等待、空载移动+抓取、重载移动+放置、避让移动、中断动作

"调度前一定设备先停下来！！！！！！！"
# 调度规则：
# 1. 完成移动作业需要进行调度的设备称为主调度设备，另一设备称为被调度设备；
# 2. 当位于同一作业状态下，轨道吊的作业优先级高于正面吊；
# 3. 当主调度设备调度优先级较高，且主调度设备与被调度设备的作业区间不重叠时，主调度设备直接执行移动作业；
# 4. 当主调度设备调度优先级较高，且主调度设备与被调度设备的作业区间重叠时，主调度设备直接执行移动作业，中断被调度设备当前作业，进行避让操作；
# 5. 当主调度设备调度优先级较低，且主调度设备与被调度设备的作业区间不重叠时，主调度设备直接执行移动作业；
# 6. 当主调度设备调度优先级较低，且主调度设备与被调度设备的作业区间重叠时，主调度设备根据具体情况，执行等待、避让操作。
# 占用状态: 0无集装箱，1有集装箱
import simpy
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import pandas as pd
import copy
import time
import enum
import matplotlib.colors as mcolors

# 状态定义：
# -2. 无任务空闲状态 / 无法作业状态
# -1. 避让状态
# 0. 有任务空闲状态
# 1. 空载移动状态
# 2. 抓取状态
# 3. 重载移动状态
# 4. 放置状态
# 5. 换线状态

# **********************************************加一个根据距离调整优先级的机制
PRIORITY = [-2, -1, 0, 5, 1, 3, 2, 4]
SafetyDistance = 1
# 环境定义
env = simpy.Environment()
# 全局事件定义
SimulationStopEvent = env.event()
ShaduleEvent = env.event()
# 轨道吊任务队列
# CraneTaskQueue =[{'StartPosition': [2,7,2], 'EndPosition': [15,7,1]}]
# ReachStackerTaskQueue =[{'StartPosition': [2,8,1], 'EndPosition': [15,9,1]}]
# CraneTaskQueue = [{'EndPosition': [12, 6, 1], 'StartPosition': [23, 11, 1]},{'EndPosition': [40, 7, 2], 'StartPosition': [37, 3, 1]},
#                   {'EndPosition': [8, 1, 1], 'StartPosition': [28, 7, 1]},
#                   {'EndPosition': [1, 11, 1], 'StartPosition': [43, 2, 2]},
#                   {'EndPosition': [3, 11, 1], 'StartPosition': [47, 1, 2]}]
# ReachStackerTaskQueue = [{'EndPosition': [51, 6, 1], 'StartPosition': [51, 4, 1]},
#                          {'EndPosition': [56, 14, 1], 'StartPosition': [16, 7, 2]},
#                          {'EndPosition': [47, 14, 1], 'StartPosition': [21, 11, 2]}]
# 优化后
# CraneTaskQueue = [{'StartPosition': [25, 9, 1], 'EndPosition': [25, 6, 1]}, {'StartPosition': [34, 3, 1], 'EndPosition': [40, 1, 2]}, {'StartPosition': [47, 8, 1], 'EndPosition': [47, 5, 1]}, {'StartPosition': [59, 10, 2], 'EndPosition': [59, 5, 1]}, {'StartPosition': [65, 3, 1], 'EndPosition': [65, 5, 1]}, {'StartPosition': [54, 3, 1], 'EndPosition': [54, 6, 1]}, {'StartPosition': [50, 4, 1], 'EndPosition': [50, 5, 1]}, {'StartPosition': [44, 1, 2], 'EndPosition': [44, 5, 1]}, {'StartPosition': [34, 9, 2], 'EndPosition': [34, 6, 1]}]
# ReachStackerTaskQueue = [{'StartPosition': [19, 12, 2], 'EndPosition': [19, 13, 1]}, {'StartPosition': [11, 12, 2], 'EndPosition': [11, 13, 1]}, {'StartPosition': [27, 9, 2], 'EndPosition': [27, 14, 1]}, {'StartPosition': [29, 7, 2], 'EndPosition': [29, 13, 1]}, {'StartPosition': [18, 10, 1], 'EndPosition': [18, 6, 1]}, {'StartPosition': [40, 7, 1], 'EndPosition': [40, 14, 1]}]
# 优化前
CraneTaskQueue =[{'StartPosition': [63, 4, 1], 'EndPosition': [63, 6, 1]}, {'StartPosition': [52, 2, 2], 'EndPosition': [52, 6, 1]}, {'StartPosition': [47, 12, 2], 'EndPosition': [47, 6, 1]}, {'StartPosition': [47, 1, 2], 'EndPosition': [47, 6, 1]}, {'StartPosition': [42, 2, 1], 'EndPosition': [42, 6, 1]}, {'StartPosition': [38, 2, 2], 'EndPosition': [38, 6, 1]}, {'StartPosition': [33, 2, 1], 'EndPosition': [33, 6, 1]}, {'StartPosition': [27, 2, 2], 'EndPosition': [27, 6, 1]}, {'StartPosition': [11, 10, 1], 'EndPosition': [11, 6, 1]}, {'StartPosition': [7, 9, 1], 'EndPosition': [7, 6, 1]}]
ReachStackerTaskQueue =[{'StartPosition': [2, 4, 1], 'EndPosition': [2, 6, 1]}, {'StartPosition': [19, 4, 1], 'EndPosition': [22, 6, 1]}, {'StartPosition': [22, 4, 1], 'EndPosition': [31, 8, 1]}, {'StartPosition': [51, 4, 1], 'EndPosition': [51, 13, 1]}, {'StartPosition': [57, 7, 2], 'EndPosition': [57, 13, 1]}, {'StartPosition': [14, 9, 1], 'EndPosition': [14, 13, 1]}, {'StartPosition': [26, 10, 2], 'EndPosition': [26, 6, 1]}, {'StartPosition': [5, 11, 1], 'EndPosition': [5, 6, 1]}, {'StartPosition': [38, 12, 2], 'EndPosition': [38, 13, 1]}, {'StartPosition': [54, 12, 1], 'EndPosition': [54, 13, 1]}]
"----------------------------工具函数-----------------------------"


def Get_MoveRange(*args: list) -> np.ndarray:
    if len(args) == 1:
        Position = args[0]
        return np.arange(Position[0] - SafetyDistance, Position[0] + 1 + SafetyDistance)
    if len(args) == 2:
        StartPoint = args[0]
        EndPoint = args[1]
        # 判断起点和终点的位置关系
        if StartPoint[0] == EndPoint[0]:
            return np.arange(StartPoint[0] - SafetyDistance, EndPoint[0] + 1 + SafetyDistance)
        elif StartPoint[0] < EndPoint[0]:
            return np.arange(StartPoint[0] - SafetyDistance, EndPoint[0] + 1 + SafetyDistance)
        else:
            return np.arange(StartPoint[0] + SafetyDistance, EndPoint[0] - 1 - SafetyDistance, -1)


# 设备死锁判断
def CheckDeviceDeadLock(ReachStacker, Crane):
    while True:
        if ReachStacker.PosRegister == [] or Crane.PosRegister == []:
            yield env.timeout(10)
            continue
        # 查询正面吊最后记录信息
        ReachStackerLastInfo = ReachStacker.PosRegister[-1]
        # 获取正面吊最后的作业位置
        ReachStackerLastPosition = ReachStackerLastInfo[0:2]
        # 查询正面吊持续最后作业位置的时间
        end = ReachStackerLastInfo[3]
        start = 0
        for elem in ReachStacker.PosRegister[::-1]:
            if elem[0:2] != ReachStackerLastPosition:
                start = elem[3]
                break
        # 计算正面吊持续最后作业状态的时间
        ReachStackerLockTime = end - start
        # 查询轨道吊最后记录信息
        CraneLastInfo = Crane.PosRegister[-1]
        # 获取轨道吊最后的作业状态
        CraneLastPosition = CraneLastInfo[0:2]
        # 查询轨道吊持续最后作业状态的时间
        end = CraneLastInfo[3]
        start = 0
        for elem in Crane.PosRegister[::-1]:
            if elem[0:2] != CraneLastPosition:
                start = elem[3]
                break
        # 计算轨道吊持续最后作业状态的时间
        CraneLockTime = end - start
        if ReachStackerLockTime > 100 and CraneLockTime > 100:
            # 判断设备死锁，抛出异常
            raise simpy.Interrupt("设备死锁")
        else:
            yield env.timeout(10)
            continue


"----------------------------绘图类-----------------------------"


class DrawPicture:
    ContainerChangeLocationList = []
    ContainerChangeLocationTimeList = []

    def __init__(self):
        self.ani = None
        self.DeviceSC = None
        self.ContainerSC1 = None
        self.ContainerSC2 = None
        self.ChangeDict = {}
        plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
        plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号
        self.fig = plt.figure()
        self.ax = self.fig.add_subplot(111)
        self.text = self.ax.text(0, 14, "时间点: %d" % 0)
        self.ax.set_xlim(1, 66)
        self.ax.set_ylim(1, 14)
        self.xtick = range(0, 66)
        self.ytick = range(0, 15)
        self.ax.set_xticks(self.xtick)
        self.ax.set_yticks(self.ytick)
        self.ax.grid(True)
        self.ax.set_aspect(1)
        self.ax.set_xlabel("贝位")
        self.ax.set_ylabel("栈位")
        self.ax.set_title("集装箱堆场")
        self.Locationlist1 = []
        self.Locationlist2 = []
        self.StateColor = {-2: 'yellow', -1: 'blue', 0: 'yellow', 1: 'green', 2: 'black', 3: 'red', 4: 'black',
                           5: 'aquamarine'}
        # self.init_containerlocation()

    def LoadData(self, loclist1, loclist2, ContainerLocationInit):
        self.ChangeDict = dict(
            zip(DrawPicture.ContainerChangeLocationTimeList, DrawPicture.ContainerChangeLocationList))
        self.Locationlist1 = loclist1
        self.Locationlist2 = loclist2
        # 寻找一层集装箱位置
        self.ContainerLocationList = ContainerLocationInit
        # 创建一个 66*15 的网格
        x = np.arange(1, 67)
        y = np.arange(1, 15)
        X, Y = np.meshgrid(x, y)
        # 集装箱散点图一层图层
        self.ContainerSC1 = self.ax.scatter([], [], s=100, marker='s', alpha=1, c='orange')
        self.ContainerSC2 = self.ax.scatter([], [], s=100, marker='s', alpha=1, c='brown')
        # 查找一层现有集装箱位置
        index = np.where(ContainerLocationInit[0][:][:] == 1)
        indices_to_remove = list(zip(index[0], index[1]))
        # 创建布尔索引
        mask = np.zeros(X.shape, dtype=bool)
        for index in indices_to_remove:
            mask[index[0], index[1]] = True
        # 应用布尔索引移除值
        X_masked = X[mask]
        Y_masked = Y[mask]
        # 将网格展平为一维数组
        x_flattened = X_masked.flatten()
        y_flattened = Y_masked.flatten()
        self.ContainerSC1.set_offsets(np.c_[x_flattened, y_flattened])
        # 查找二层现有集装箱位置
        index = np.where(ContainerLocationInit[1][:][:] == 1)
        indices_to_remove = list(zip(index[0], index[1]))
        # 创建布尔索引
        mask = np.zeros(X.shape, dtype=bool)
        for index in indices_to_remove:
            mask[index[0], index[1]] = True
        # 应用布尔索引移除值
        X_masked = X[mask]
        Y_masked = Y[mask]
        # 将网格展平为一维数组
        x_flattened = X_masked.flatten()
        y_flattened = Y_masked.flatten()
        self.ContainerSC2.set_offsets(np.c_[x_flattened, y_flattened])
        # 设备散点图图层
        self.DeviceSC = self.ax.scatter([], [], s=70, marker='s')
        self.ani = FuncAnimation(self.fig, self.update, frames=len(self.Locationlist1) + 1, interval=10, blit=False,
                                 repeat=False)  # 创建动画效果

    def update(self, frame):
        # 时间点提示向左偏移
        if self.Locationlist1[frame][3] in self.ChangeDict:
            x = np.arange(1, 67)
            y = np.arange(1, 15)
            X, Y = np.meshgrid(x, y)
            # 查找一层现有集装箱位置
            index = np.where(self.ChangeDict[self.Locationlist1[frame][3]][0][:][:] == 1)
            indices_to_remove = list(zip(index[0], index[1]))
            # 创建布尔索引
            mask = np.zeros(X.shape, dtype=bool)
            for index in indices_to_remove:
                mask[index[0], index[1]] = True
            # 应用布尔索引移除值
            X_masked = X[mask]
            Y_masked = Y[mask]
            # 将网格展平为一维数组
            x_flattened = X_masked.flatten()
            y_flattened = Y_masked.flatten()
            self.ContainerSC1.set_offsets(np.c_[x_flattened, y_flattened])
            # 查找二层现有集装箱位置
            index = np.where(self.ChangeDict[self.Locationlist1[frame][3]][1][:][:] == 1)
            indices_to_remove = list(zip(index[0], index[1]))
            # 创建布尔索引
            mask = np.zeros(X.shape, dtype=bool)
            for index in indices_to_remove:
                mask[index[0], index[1]] = True
            # 应用布尔索引移除值
            X_masked = X[mask]
            Y_masked = Y[mask]
            # 将网格展平为一维数组
            x_flattened = X_masked.flatten()
            y_flattened = Y_masked.flatten()
            self.ContainerSC2.set_offsets(np.c_[x_flattened, y_flattened])
        self.text.set_text("时间点: %d" % self.Locationlist1[frame][3])
        x1 = self.Locationlist1[frame][0]
        y1 = self.Locationlist1[frame][1]
        Point1color = self.StateColor[self.Locationlist1[frame][2]]
        Point1color = mcolors.to_rgba(Point1color)
        x2 = self.Locationlist2[frame][0]
        y2 = self.Locationlist2[frame][1]
        Point2color = self.StateColor[self.Locationlist2[frame][2]]
        Point2color = mcolors.to_rgba(Point2color)
        self.DeviceSC.set_offsets([[x1, y1], [x2, y2]])
        self.DeviceSC.set_facecolors([Point1color, Point2color])


"----------------------------枚举类-----------------------------"


class WorkLinePos(enum.Enum):
    Up = 1
    Down = 2


"----------------------------集装箱箱位类-----------------------------"


class ContainerLocation:
    ContainerList = []
    ContainerListCopy = []

    def __init__(self, bay, stack, level, state, Locationtype):
        self.bay = bay  # 贝位
        self.level = level  # 层位
        self.stack = stack  # 栈位
        self.state = state  # 集装箱箱位状态: 放置货物 1 没有货物 0
        self.Locationtype = Locationtype  # 箱位类型: 0 普通箱位 1 道路箱位
        ContainerLocation.ContainerList.append(self)

    # 根据箱位号查询箱位对象列表
    @classmethod
    def FindContainerLocation(cls, bay, stack, level):
        return [x for x in cls.ContainerList if (x.stack == stack and x.level == level and x.bay == bay)]

    @classmethod
    def ContainerLocationListSave(cls):
        cls.ContainerListCopy.clear()
        cls.ContainerListCopy = copy.deepcopy(cls.ContainerList)
        return True

    @classmethod
    def ContainerLocationListRecover(cls):
        cls.ContainerList.clear()
        cls.ContainerList = copy.deepcopy(cls.ContainerListCopy)
        return True

    # 判断集装箱位置是否可抓取
    def CheckContainerLocationPick(self):
        if self.Locationtype == 1:
            return True
        # 如果当前货位上有货物，进一步判断
        if self.state == 1:
            # 如果当前货位处于二层，可以抓取
            if self.level == 2:
                return True
            # 如果当前货位处于一层，判断是否为列车箱位
            if self.level == 1 and self.Locationtype == 2:
                return True
            elif self.level == 1 and self.Locationtype != 2:
                try:
                    # 获取二层货位对象
                    ContainerLocationSelect = ContainerLocation.FindContainerLocation(self.bay,
                                                                                                self.stack,
                                                                                                self.level + 1).pop()
                except IndexError:
                    return True
                # 如果二层货位上有货物不可抓取，否则可以抓取
                if ContainerLocationSelect.state == 1:
                    return False
                else:
                    return True
        # 如果当前货位上没有货物，不可抓取
        else:
            return False

    def CheckContainerLocationPut(self):
        if self.Locationtype == 1:
            return True
        # 如果当前货位上没有货物，进一步判断
        if self.state == 0:
            # 如果当前货位处于一层，可以放置
            if self.level == 1:
                return True
            # 如果当前货位处于二层，判断一层是否有货物
            elif self.level == 2:
                # 获取二层货位对象
                ContainerLocationSelect = ContainerLocation.FindContainerLocation(self.bay, self.stack,
                                                                                  self.level - 1).pop()
                # 如果一层货位上有货物则可以放置，否则不可放置
                if ContainerLocationSelect.state == 1:
                    return True
                else:
                    return False
        # 如果当前货位上有货物，不可放置
        else:
            return False

    # 计算正面吊可以停靠的位置
    def DockingLocationSearch(self):
        TargetBay = self.bay
        # 获得目标位置的贝位和栈位
        TargetStack = self.stack
        # 栈位超出范围，无解，给出异常
        assert 3 < TargetStack < 15
        # 通道规则
        if TargetStack == 4:
            return [None, [TargetBay, 5]]
        if TargetStack == 5:
            return [None, [TargetBay, 6]]
        if TargetStack == 6:
            return [None, [TargetBay, 5]]
        if TargetStack == 13:
            return [[TargetBay, 14], None]
        if TargetStack == 14:
            return [[TargetBay, 13], None]
        MovePathUp = []
        MovePathDown = []
        CheckStack = TargetStack
        while True:
            # 向上侧查找，先判断是否为道路,若为道路,则将检查位置加入路径，退出
            CheckStack += 1
            ContainerLocationSelect = ContainerLocation.FindContainerLocation(TargetBay, CheckStack, 1).pop()
            if ContainerLocationSelect.Locationtype == 1:
                MovePathUp.append(CheckStack)
                break
            # 若不为道路，则检查当前位置一层是否有集装箱，若有则不能成为可用路径
            else:
                if ContainerLocationSelect.state == 0:
                    MovePathUp.append(CheckStack)
                    continue
                else:
                    # 判断是否为目标栈位的邻接栈位，若是则检查二层是否有集装箱，若没有则继续搜索，否则直接退出
                    if abs(CheckStack - TargetStack) == 1:
                        if ContainerLocation.FindContainerLocation(TargetBay, CheckStack, 2).pop().state <= \
                                ContainerLocation.FindContainerLocation(TargetBay, TargetStack, 2).pop().state:
                            continue
                        else:
                            MovePathUp.clear()
                            break
                    else:
                        MovePathUp.clear()
                        break
        CheckStack = TargetStack
        while True:
            # 向下侧查找，先判断是否为道路,若为道路,则将检查位置加入路径，退出
            CheckStack -= 1
            ContainerLocationSelect = ContainerLocation.FindContainerLocation(TargetBay, CheckStack, 1).pop()
            if ContainerLocationSelect.Locationtype == 1:
                MovePathDown.append(CheckStack)
                break
            # 若不为道路，则检查当前位置一层是否有集装箱，若有则不能成为可用路径
            else:
                if ContainerLocationSelect.state == 0:
                    MovePathDown.append(CheckStack)
                    continue
                else:
                    # 判断是否为目标栈位的邻接栈位，若是则检查二层是否有集装箱，若没有则继续搜索，否则直接退出
                    if abs(CheckStack - TargetStack) == 1:
                        if ContainerLocation.FindContainerLocation(TargetBay, CheckStack, 2).pop().state <= \
                                ContainerLocation.FindContainerLocation(TargetBay, TargetStack, 2).pop().state:
                            continue
                        else:
                            MovePathDown.clear()
                            break
                    else:
                        MovePathDown.clear()
                        break
        if len(MovePathUp) != 0 or len(MovePathDown) != 0:
            if len(MovePathUp) != 0:
                MovePathUp = np.array(MovePathUp)
                DockingLocationUp = [TargetBay, np.min(MovePathUp)]
            else:
                DockingLocationUp = None
            if len(MovePathDown) != 0:
                MovePathDown = np.array(MovePathDown)
                DockingLocationDown = [TargetBay, np.max(MovePathDown)]
            else:
                DockingLocationDown = None
            return [DockingLocationUp, DockingLocationDown]
        else:
            return False

    def PlacingFeasibilityQueries(self):
        TargetBay = self.bay
        TargetStack = self.stack
        if TargetStack < 7 or TargetStack > 12:
            return True
        else:
            # 初始化集装箱位置列表
            ContainerLocationInit = []
            for stack in range(7, 13):
                ContainerLocationInit.append(ContainerLocation.FindContainerLocation(TargetBay, stack, 1).pop())
            # 记录活动集装箱位置（即正面吊所涉及的所有集装箱位置）
            ActiveContainerLocation = []
            for item in ReachStackerTaskQueue:
                for key, value in item.items():
                    selectselectContainerLocation = ContainerLocation.FindContainerLocation(value[0], value[1], 1).pop()
                    if (selectselectContainerLocation.state == 1 and key == "StartPosition") or key == "EndPosition":
                        ActiveContainerLocation.append(
                            ContainerLocation.FindContainerLocation(value[0], value[1], 1).pop())
                    else:
                        continue
            # 删除活动集装箱位置，剩下的即为固定集装箱位置（即正面吊不涉及的集装箱位置）
            for item in ActiveContainerLocation:
                try:
                    ContainerLocationInit.remove(item)
                except ValueError:
                    continue
            FixedContainerPosition = ContainerLocationInit.copy()
            TargetBay = self.bay
            TargetStack = self.stack
            CheckStack = TargetStack
            FixContainerexistFlag = False
            ActiveContainerexistFlag = False
            while True:
                # 向上侧查找，先判断是否为道路,若为道路,则将检查位置加入路径，退出
                CheckStack += 1
                ContainerLocationSelect = ContainerLocation.FindContainerLocation(TargetBay, CheckStack, 1).pop()
                if ContainerLocationSelect.Locationtype == 1:
                    break
                else:
                    if ContainerLocationSelect in FixedContainerPosition:
                        FixContainerexistFlag = True
                    if ContainerLocationSelect in ActiveContainerLocation:
                        ActiveContainerexistFlag = True
            if FixContainerexistFlag == True and ActiveContainerexistFlag == True:
                return False
            else:
                pass
            FixContainerexistFlag = False
            ActiveContainerexistFlag = False
            CheckStack = TargetStack
            while True:
                # 向下侧查找，先判断是否为道路,若为道路则退出
                CheckStack -= 1
                ContainerLocationSelect = ContainerLocation.FindContainerLocation(TargetBay, CheckStack, 1).pop()
                if ContainerLocationSelect.Locationtype == 1:
                    break
                else:
                    if ContainerLocationSelect in FixedContainerPosition:
                        FixContainerexistFlag = True
                    if ContainerLocationSelect in ActiveContainerLocation:
                        ActiveContainerexistFlag = True
            if FixContainerexistFlag == True and ActiveContainerexistFlag == True:
                return False
            else:
                return True


"----------------------------调度控制器类-----------------------------"


class ControllStation:
    DataStack = []

    def __init__(self, env):
        self.env = env
        self.env.process(self.main())
        self.env.process(self.running())
        self.ShaduleDevice = []

    def running(self):
        yield SimulationStopEvent
        return True

    @staticmethod
    # 作业调度函数
    def Schedule(Device):
        """-----------------------------------标志位---------------------------------------"""
        # 优先级标志位
        PriorityFlag = False
        # 作业交叉标志位
        CrossFlag = False
        # 主调度设备可作业标志位
        MainShaduleDeviceWorkFeasibility = True
        """-----------------------------------中间变量--------------------------------------"""
        # 调度数据栈
        ScheduleDataStack = []

        # 指定主调度设备与被调度设备
        MainShaduleDevice = Device
        BeShaduleDevice = ReachStacker if Device == Crane else Crane
        # 获取主调度设备基本作业信息
        MainShaduleDeviceStateCode = MainShaduleDevice.StateCode
        MainShaduleDeviceCurrentPos = MainShaduleDevice.CurrentPos
        MainShaduleDeviceCurrentTask = MainShaduleDevice.CurrentTask
        MainShaduleDeviceTargetPos = MainShaduleDevice.TargetPos
        MainShaduleDevicePriority = PRIORITY.index(MainShaduleDeviceStateCode)
        # 获取被调度设备基本作业信息
        BeShaduleDeviceStateCode = BeShaduleDevice.StateCode
        BeShaduleDeviceCurrentPos = BeShaduleDevice.CurrentPos
        BeShaduleDeviceCurrentTask = BeShaduleDevice.CurrentTask
        BeShaduleDeviceTargetPos = BeShaduleDevice.TargetPos
        BeShaduleDevicePriority = PRIORITY.index(BeShaduleDeviceStateCode)

        """----------------------------------------作业可行性判断----------------------------------"""
        # 判断主调度设备是否为正面吊
        if MainShaduleDevice is ReachStacker:
            # 判断正面吊作业的作业状态
            if not MainShaduleDevice.LoadOn:
                MainShaduleDevice.TargetPos = MainShaduleDevice.CurrentTask["StartPosition"]
                # 判断该位置是否能抓取
                PickUPContainerLocation = ContainerLocation.FindContainerLocation(MainShaduleDevice.TargetPos[0],
                                                                                  MainShaduleDevice.TargetPos[1],
                                                                                  MainShaduleDevice.TargetPos[2]).pop()
                # 若该位置不能抓取，等待
                if not PickUPContainerLocation.CheckContainerLocationPick():
                    # 主调度作业设备不可作业
                    MainShaduleDevice.StateCode = -1
                    MainShaduleDevice.AvoidancePos = MainShaduleDevice.CurrentPos
                    MainShaduleDeviceWorkFeasibility = False
                else:
                    # 主调度作业设备可作业
                    MainShaduleDevice.StateCode = 0
                    MainShaduleDeviceWorkFeasibility = True
            else:
                MainShaduleDevice.TargetPos = MainShaduleDevice.CurrentTask["EndPosition"]
                # 判断该位置是否能放置
                PutContainerLocation = ContainerLocation.FindContainerLocation(MainShaduleDevice.TargetPos[0],
                                                                               MainShaduleDevice.TargetPos[1],
                                                                               MainShaduleDevice.TargetPos[2]).pop()
                # 若该位置不能放置，等待
                if not PutContainerLocation.CheckContainerLocationPut():
                    # 主调度作业设备不可作业
                    MainShaduleDevice.StateCode = -1
                    MainShaduleDevice.AvoidancePos = MainShaduleDevice.CurrentPos
                    MainShaduleDeviceWorkFeasibility = False
                else:
                    # 主调度作业设备可作业
                    MainShaduleDevice.StateCode = 0
                    MainShaduleDeviceWorkFeasibility = True
            # 获得正面吊的停靠位置
            DockingLocation = ContainerLocation.FindContainerLocation(MainShaduleDevice.TargetPos[0],
                                                                      MainShaduleDevice.TargetPos[1],
                                                                      MainShaduleDevice.TargetPos[
                                                                          2]).pop().DockingLocationSearch()
            # 无停靠位置，等待or避让
            if DockingLocation is False:
                # 主调度作业设备不可作业
                MainShaduleDevice.StateCode = -1
                MainShaduleDevice.AvoidancePos = MainShaduleDevice.CurrentPos
                MainShaduleDeviceWorkFeasibility = False
            else:
                # 主调度作业设备可作业
                MainShaduleDevice.StateCode = 0
                MainShaduleDeviceWorkFeasibility = True

            # 有停靠位置
            if MainShaduleDevice.Worklinepos == WorkLinePos.Down and DockingLocation is not False:
                DockingLocation_ = DockingLocation[1]
                if DockingLocation_ is None:
                    ControllStation.DataStack.append(DockingLocation[0])
                    MainShaduleDevice.ReplaceWorkLineEvent.succeed()
                    MainShaduleDevice.ReplaceWorkLineEvent = MainShaduleDevice.env.event()
                    return True
                else:
                    MainShaduleDevice.TargetPos = DockingLocation_
                    MainShaduleDeviceTargetPos = DockingLocation_

            elif MainShaduleDevice.Worklinepos == WorkLinePos.Up and DockingLocation is not False:
                DockingLocation_ = DockingLocation[0]
                if DockingLocation_ is None:
                    ControllStation.DataStack.append(DockingLocation[1])
                    MainShaduleDevice.ReplaceWorkLineEvent.succeed()
                    MainShaduleDevice.ReplaceWorkLineEvent = MainShaduleDevice.env.event()
                    return True
                else:
                    MainShaduleDevice.TargetPos = DockingLocation_
                    MainShaduleDeviceTargetPos = DockingLocation_
        # 判断主调度设备是否为轨道吊
        else:
            # 轨道吊是否处于空载作业状态
            if not MainShaduleDevice.LoadOn:
                MainShaduleDeviceTargetPos = MainShaduleDevice.CurrentTask["StartPosition"]
                MainShaduleDevice.TargetPos = MainShaduleDevice.CurrentTask["StartPosition"]
                # 判断该位置是否能抓取
                PickUPContainerLocation = ContainerLocation.FindContainerLocation(MainShaduleDeviceTargetPos[0],
                                                                                  MainShaduleDeviceTargetPos[1],
                                                                                  MainShaduleDeviceTargetPos[2]).pop()
                # 若该位置不能抓取，等待
                if not PickUPContainerLocation.CheckContainerLocationPick():
                    # 将主调度设备调度状态改为避让状态
                    MainShaduleDevice.StateCode = -1
                    MainShaduleDevice.AvoidancePos = MainShaduleDevice.CurrentPos
                    MainShaduleDeviceWorkFeasibility = False
                else:
                    # 恢复主调度设备作业状态
                    MainShaduleDevice.StateCode = 0
                    MainShaduleDeviceWorkFeasibility = True

            else:
                MainShaduleDeviceTargetPos = MainShaduleDevice.CurrentTask["EndPosition"]
                MainShaduleDevice.TargetPos = MainShaduleDevice.CurrentTask["EndPosition"]
                # 判断该位置是否能放置
                PutContainerLocation = ContainerLocation.FindContainerLocation(MainShaduleDeviceTargetPos[0],
                                                                               MainShaduleDeviceTargetPos[1],
                                                                               MainShaduleDeviceTargetPos[2]).pop()

                if not PutContainerLocation.PlacingFeasibilityQueries() or not PutContainerLocation.CheckContainerLocationPut():
                    # 将主调度设备调度状态改为避让状态
                    MainShaduleDevice.StateCode = -1
                    MainShaduleDevice.AvoidancePos = MainShaduleDevice.CurrentPos
                    MainShaduleDeviceWorkFeasibility = False
                else:
                    # 恢复主调度设备作业状态
                    MainShaduleDevice.StateCode = 0
                    MainShaduleDeviceWorkFeasibility = True
        # 获取主调度设备的预计作业区间
        MainShaduleDeviceMoveRange = None
        if MainShaduleDevice.StateCode == 0:
            MainShaduleDeviceMoveRange = Get_MoveRange(MainShaduleDevice.CurrentPos, MainShaduleDevice.TargetPos)
        elif MainShaduleDevice.StateCode == -1:
            MainShaduleDeviceMoveRange = Get_MoveRange(MainShaduleDevice.CurrentPos, MainShaduleDevice.AvoidancePos)
        elif MainShaduleDevice.StateCode == -2:
            MainShaduleDeviceMoveRange = Get_MoveRange(MainShaduleDevice.CurrentPos)
        # 获取被调度设备的正在作业区间
        BeShaduleDeviceMoveRange = None
        if BeShaduleDevice.StateCode == 0 or BeShaduleDevice.StateCode == 1 or BeShaduleDevice.StateCode == 3 \
                or BeShaduleDevice.StateCode == 5:
            BeShaduleDeviceMoveRange = Get_MoveRange(BeShaduleDevice.CurrentPos,
                                                     BeShaduleDevice.TargetPos)
        elif BeShaduleDevice.StateCode == 2 or BeShaduleDevice.StateCode == 4 or BeShaduleDevice.StateCode == -2:
            BeShaduleDeviceMoveRange = Get_MoveRange(BeShaduleDevice.CurrentPos)
        elif BeShaduleDeviceStateCode == -1:
            BeShaduleDeviceMoveRange = Get_MoveRange(BeShaduleDevice.CurrentPos, BeShaduleDevice.AvoidancePos)
        min_val = 0
        max_val = 65
        # 判断主调度设备作业优先级是否大于被调度设备
        if MainShaduleDevice.StateCode == 0 and MainShaduleDevice.LoadOn == True:
            # 获得当前贝位与目标贝位的距离差并归一化
            Distance = abs(MainShaduleDevice.CurrentPos[0] - MainShaduleDevice.TargetPos[0])
            alpha = 0.5 - (0.5 * (Distance - min_val) / (max_val - min_val))
            MainShaduleDevicePriority = PRIORITY.index(MainShaduleDevice.StateCode) + 0.5 + alpha
        else:
            Distance = abs(MainShaduleDevice.CurrentPos[0] - MainShaduleDevice.TargetPos[0])
            alpha = 0.5 - (0.5 * (Distance - min_val) / (max_val - min_val))
            MainShaduleDevicePriority = PRIORITY.index(MainShaduleDevice.StateCode) + alpha
        if BeShaduleDevice.StateCode == 0 and BeShaduleDevice.LoadOn == True:
            Distance = abs(BeShaduleDevice.CurrentPos[0] - BeShaduleDevice.TargetPos[0])
            alpha = 0.5 - (0.5 * (Distance - min_val) / (max_val - min_val))
            BeShaduleDevicePriority = PRIORITY.index(BeShaduleDevice.StateCode) + 0.5 + alpha
        else:
            Distance = abs(BeShaduleDevice.CurrentPos[0] - BeShaduleDevice.TargetPos[0])
            alpha = 0.5 - (0.5 * (Distance - min_val) / (max_val - min_val))
            BeShaduleDevicePriority = PRIORITY.index(BeShaduleDevice.StateCode) + alpha
        if MainShaduleDevicePriority > BeShaduleDevicePriority:
            PriorityFlag = True
        elif MainShaduleDevicePriority == BeShaduleDevicePriority and MainShaduleDevice == Crane:
            PriorityFlag = True
        else:
            PriorityFlag = False
        # 判断主调度设备与被调度设备是否交叉
        if ReachStacker.Worklinepos == WorkLinePos.Down and len(
                np.intersect1d(MainShaduleDeviceMoveRange, BeShaduleDeviceMoveRange)) > 1:
            CrossFlag = True
        else:
            CrossFlag = False

        """---------------------------------------执行作业-------------------------------------------------------"""
        # if MainShaduleDeviceWorkFeasibility is False:
        #     MainShaduleDevice.WaitEvent.succeed()
        #     return True
        # 主调度设备优先级高且两设备不存在路径重叠情形，主调度设备直接作业
        if PriorityFlag == True and CrossFlag == False:
            MainShaduleDevice.MoveEvent.succeed()
            MainShaduleDevice.MoveEvent = env.event()
        # 主调度设备优先级低且两设备不存在路径重叠情形，主调度设备直接作业
        if PriorityFlag == False and CrossFlag == False and MainShaduleDeviceWorkFeasibility is True:
            MainShaduleDevice.MoveEvent.succeed()
            MainShaduleDevice.MoveEvent = env.event()
        if PriorityFlag == False and CrossFlag == False and MainShaduleDeviceWorkFeasibility is False:
            MainShaduleDevice.WaitEvent.succeed()
            MainShaduleDevice.WaitEvent = env.event()
        # 主调度设备优先级高且两设备存在路径重叠情形，被调度设备作业中断且进行避让，主调度设备优先作业
        if PriorityFlag == True and CrossFlag == True and BeShaduleDevice.DeviceLock == True:
            MainShaduleDevice.MoveEvent.succeed()
            MainShaduleDevice.MoveEvent = env.event()
        elif PriorityFlag == True and CrossFlag == True and BeShaduleDevice.DeviceLock == False:
            MainShaduleDevice.MoveEvent.succeed()
            MainShaduleDevice.MoveEvent = env.event()
            # 判断被动调度设备是否需要避让，即被动调度设备当前位置是否在主调度设备的预计作业区间内
            if BeShaduleDevice.CurrentPos[0] in MainShaduleDeviceMoveRange:
                if BeShaduleDevice == Crane:
                    Avoidanceloc = [np.max(MainShaduleDeviceMoveRange) + 1, BeShaduleDevice.CurrentPos[1]]
                else:
                    Avoidanceloc = [np.min(MainShaduleDeviceMoveRange) - 1, BeShaduleDevice.CurrentPos[1]]
                ControllStation.Avoidance(BeShaduleDevice, Avoidanceloc)
            else:
                ControllStation.Wait(BeShaduleDevice)

        # 主调度设备优先级低且两设备存在路径重叠情形，判断主调度设备是否碍事，若碍事则避让，否则原地等待
        if PriorityFlag == False and CrossFlag == True and MainShaduleDevice.DeviceLock == True:
            pass
        elif PriorityFlag == False and CrossFlag == True and MainShaduleDevice.DeviceLock == False:
            CrossRange = np.intersect1d(MainShaduleDeviceMoveRange, BeShaduleDeviceMoveRange)
            if MainShaduleDevice.CurrentPos[0] in CrossRange:
                if MainShaduleDevice == Crane:
                    Avoidanceloc = [np.max(BeShaduleDeviceMoveRange) + 1, MainShaduleDevice.CurrentPos[1]]
                else:
                    Avoidanceloc = [np.min(BeShaduleDeviceMoveRange) - 1, MainShaduleDevice.CurrentPos[1]]
                ControllStation.Avoidance(MainShaduleDevice, Avoidanceloc)
            else:
                ControllStation.Wait(MainShaduleDevice)

    @staticmethod
    def Avoidance(obj, loc):
        "避让作业"
        obj.InterruptWorkEvent.succeed()
        obj.InterruptWorkEvent = env.event()
        # 避让到指定位置
        obj.AvoidancePos = loc
        obj.AvoidanceEvent.succeed()
        obj.AvoidanceEvent = env.event()
        obj.DeviceLock = True
        return True

    @staticmethod
    def Wait(obj):
        "等待作业"
        obj.InterruptWorkEvent.succeed()
        obj.InterruptWorkEvent = env.event()
        obj.WaitEvent.succeed()
        obj.WaitEvent = env.event()
        obj.DeviceLock = True
        return True

    def main(self):
        global ShaduleEvent
        CraneShaduleCount = 1
        ReachStackerShaduleCount = 1
        while True:
            "----------------------------任务分配-----------------------------"
            # 获取轨道吊相关信息
            CraneInfo = Crane.GetInfo("Crane")
            # 获取正面吊相关信息
            ReachStackerInfo = ReachStacker.GetInfo("ReachStacker")
            if CraneInfo["CurrentTask"] == {} and CraneTaskQueue != [] and CraneInfo["StateCode"] == -2:
                # Crane.CurrentTask = CraneTaskQueue.pop(0)
                Crane.CurrentTask = CraneTaskQueue[0]
                Crane.StateCode = 0
                CraneShaduleCount += 1
                self.ShaduleDevice.append(Crane)
            elif CraneInfo["CurrentTask"] == {} and CraneTaskQueue == []:
                Crane.StateCode = -2
            if ReachStackerInfo["CurrentTask"] == {} and ReachStackerTaskQueue != [] and ReachStackerInfo[
                "StateCode"] == -2:
                # ReachStacker.CurrentTask = ReachStackerTaskQueue.pop(0)
                ReachStacker.CurrentTask = ReachStackerTaskQueue[0]
                ReachStacker.StateCode = 0
                ReachStackerShaduleCount += 1
                self.ShaduleDevice.append(ReachStacker)
            elif ReachStackerInfo["CurrentTask"] == {} and ReachStackerTaskQueue == []:
                ReachStacker.StateCode = -2
            for Device in self.ShaduleDevice:
                self.Schedule(Device)
                print("{}调度，当前设备状态为{}".format(Device.name, Device.StateCode))
            self.ShaduleDevice.clear()
            "----------------------------仿真结束条件-----------------------------"
            # 获取轨道吊相关信息
            CraneInfo = Crane.GetInfo("Crane")
            # 获取正面吊相关信息
            ReachStackerInfo = ReachStacker.GetInfo("ReachStacker")
            if CraneTaskQueue == [] and ReachStackerTaskQueue == [] and CraneInfo["CurrentTask"] == {} and \
                    ReachStackerInfo["CurrentTask"] == {}:
                SimulationStopEvent.succeed()
            else:
                yield ShaduleEvent
                Device = ShaduleEvent.value
                if Device.CurrentTask != {}:
                    self.ShaduleDevice.append(ShaduleEvent.value)
                ShaduleEvent = env.event()


"----------------------------正面吊类-----------------------------"


class ReachStacker(simpy.Resource):
    ReachtSackerName = dict()

    def __init__(self, env, name):
        super().__init__(env)
        self.name = name
        self.env = env
        self.CurrentTask = {}  # 记录当前作业的任务
        self.CurrentPos = [0, 6]  # 记录正面吊当前位置
        self.AvoidancePos = []  # 记录避让位置
        self.StateCode = -2
        self.GANTRY_SPEED = 3  # 移动一个贝位所需要的时间
        self.TROLLEY_SPEED = 3  # 移动一个栈位所需要的时间
        self.PICK_TIME = 50
        self.PUT_TIME = 50
        self.Safe_Distance = SafetyDistance  # 安全距离
        self.TargetPos = [0, 6]
        self.EnterPos = []
        self.InterruptFlag = False
        self.PosRegister = []  # 记录正面吊大车位置
        self.Worklinepos = WorkLinePos.Down
        self.DataStack = []
        ReachStacker.ReachtSackerName[self.name] = self

        "----------------------------标志位定义-----------------------------"
        self.GantryMoveingFlag = False
        self.TrolleyMoveingFlag = False
        self.DeviceLock = False  # 设备锁
        self.LoadOn = False  # 负载状态

        "----------------------------事件定义-----------------------------"
        self.GantryMoveEvent = self.env.event()  # 大车开始移动事件
        self.GantryMoveOverEvent = self.env.event()  # 大车移动结束事件
        self.TrolleyMoveEvent = self.env.event()  # 小车开始移动事件
        self.TrolleyMoveOverEvent = self.env.event()  # 小车移动结束事件
        self.MoveEvent = self.env.event()  # 开始移动事件
        self.PickEvent = self.env.event()  # 抓取事件
        self.PutEvent = self.env.event()  # 放置事件
        self.AvoidanceEvent = self.env.event()  # 避让事件
        self.AvoidanceOverEvent = self.env.event()  # 避让完成事件
        self.WaitEvent = self.env.event()  # 等待事件
        self.ReplaceWorkLineEvent = self.env.event()  # 更换作业线事件
        self.ReplaceWorkLineOverEvent = self.env.event()  # 更换作业线完成事件
        self.InterruptWorkEvent = self.env.event()
        self.GantryMoveProcessInterruptOver = self.env.event()
        self.TrolleyMoveProcessInterruptOver = self.env.event()

        "----------------------------进程定义-----------------------------"
        self.GantryMoveProcess = self.env.process(self.GantryMove())
        self.TrolleyMoveProcess = self.env.process(self.TrolleyMove())
        self.env.process(self.Move())
        self.env.process(self.Pick())
        self.env.process(self.Put())
        self.env.process(self.AvoidanceMove())
        self.env.process(self.Wait())
        self.env.process(self.LocationRecord())
        self.env.process(self.ReplaceWorkLine())
        self.env.process(self.InterruptWork())

    def LocationRecord(self):
        "记录位置"
        while True:
            yield self.env.timeout(1)
            self.PosRegister.append([self.CurrentPos[0], self.CurrentPos[1], self.StateCode, self.env.now])

    @classmethod
    def GetInfo(cls, name):
        reachtSacker = cls.ReachtSackerName[name]
        infodict = dict()
        infodict['StateCode'] = reachtSacker.StateCode
        infodict['CurrentPos'] = reachtSacker.CurrentPos
        infodict['CurrentTask'] = reachtSacker.CurrentTask
        return infodict

    def AvoidanceMove(self):
        while True:
            yield self.AvoidanceEvent
            self.AvoidanceEvent = self.env.event()  # 避让事件重置
            # 保留当前作业状态
            StateCode = self.StateCode
            # 将作业状态设为-1，表示避让
            self.StateCode = -1
            self.TargetPos = self.AvoidancePos
            self.GantryMoveEvent.succeed()
            self.GantryMoveEvent = self.env.event()
            self.TrolleyMoveEvent.succeed()
            self.TrolleyMoveEvent = self.env.event()
            yield self.GantryMoveOverEvent & self.TrolleyMoveOverEvent
            # 将作业状态设为有任务空闲状态
            self.StateCode = 0
            while True:
                try:
                    ShaduleEvent.succeed(self)
                    break
                except:
                    yield env.timeout(1)
            self.DeviceLock = False

    def ReplaceWorkLine(self):
        while True:
            yield self.ReplaceWorkLineEvent
            self.ReplaceWorkLineEvent = self.env.event()
            # 将作业状态设为5，表示换线
            self.StateCode = 5
            if self.CurrentPos[1] == 5 or self.CurrentPos[1] == 6:
                self.TargetPos = [0, 14]
            elif self.CurrentPos[1] == 13 or self.CurrentPos[1] == 14:
                self.TargetPos = [0, 5]
            self.GantryMoveEvent.succeed()
            yield self.GantryMoveOverEvent
            self.TrolleyMoveEvent.succeed()
            yield self.TrolleyMoveOverEvent
            self.ReplaceWorkLineOverEvent.succeed()
            self.ReplaceWorkLineOverEvent = self.env.event()
            self.TargetPos = ControllStation.DataStack.pop()
            self.StateCode = 0
            while True:
                try:
                    ShaduleEvent.succeed(self)
                    break
                except:
                    yield env.timeout(1)

    def InterruptWork(self):
        while True:
            yield self.InterruptWorkEvent
            if self.GantryMoveingFlag:
                self.GantryMoveProcess.interrupt()
                yield self.GantryMoveProcessInterruptOver
                self.GantryMoveProcessInterruptOver = self.env.event()
            if self.TrolleyMoveingFlag:
                self.TrolleyMoveProcess.interrupt()
                yield self.TrolleyMoveProcessInterruptOver
                self.TrolleyMoveProcessInterruptOver = self.env.event()

    def Move(self):
        while True:
            # 抛出开始移动事件，等待事件触发
            yield self.MoveEvent
            # 移动开始事件触发
            # 移动开始事件复位
            self.MoveEvent = self.env.event()
            # 判断当前作业状态，如果作业状态为等待调度状态且没有装载货物，进入下面程序块
            if self.StateCode == 0 and self.LoadOn == False:
                # 将作业状态设为1，表示正在空载移动
                self.StateCode = 1
                # 大车移动开始事件触发
                self.GantryMoveEvent.succeed()
                # 大车移动开始事件复位
                self.GantryMoveEvent = self.env.event()
                # 等待大车移动结束事件触发
                yield self.GantryMoveOverEvent
                # 异常处理，如果遇到中断事件，将中断标志位设为True，跳出循环
                if self.InterruptFlag:
                    self.InterruptFlag = False
                    self.MoveEvent = self.env.event()
                    continue
                # 记录进入通道的位置
                self.EnterPos = copy.deepcopy(self.CurrentPos)
                # 小车移动开始事件触发
                self.TrolleyMoveEvent.succeed()
                # 小车移动开始事件复位
                self.TrolleyMoveEvent = self.env.event()
                # 等待小车移动结束事件触发
                yield self.TrolleyMoveOverEvent
                # 异常处理，如果遇到中断事件，将中断标志位设为True，跳出循环
                if self.InterruptFlag:
                    self.InterruptFlag = False
                    self.MoveEvent = self.env.event()
                    continue
                # 抓取事件触发
                self.PickEvent.succeed()
                # 抓取事件复位
                self.PickEvent = self.env.event()

            # 判断当前作业状态，如果作业状态为等待调度状态且装载货物，进入下面程序块
            if self.StateCode == 0 and self.LoadOn == True:
                # 将作业状态设为3，表示正在重载移动
                self.StateCode = 3
                # 大车移动开始事件触发
                self.GantryMoveEvent.succeed()
                # 大车移动开始事件复位
                self.GantryMoveEvent = self.env.event()
                # 等待大车移动结束事件触发
                yield self.GantryMoveOverEvent
                # 异常处理，如果遇到中断事件，将中断标志位设为True，跳出循环
                if self.InterruptFlag:
                    self.InterruptFlag = False
                    self.MoveEvent = self.env.event()
                    self.StateCode = 0
                    continue
                # 记录进入通道的栈位
                self.EnterPos = copy.deepcopy(self.CurrentPos)
                # 小车移动开始事件触发
                self.TrolleyMoveEvent.succeed()
                # 小车移动开始事件复位
                self.TrolleyMoveEvent = self.env.event()
                # 等待小车移动结束事件触发
                yield self.TrolleyMoveOverEvent
                # 异常处理，如果遇到中断事件，将中断标志位设为True，跳出循环
                if self.InterruptFlag:
                    self.InterruptFlag = False
                    self.MoveEvent = self.env.event()
                    self.StateCode = 0
                    continue
                # 放置事件触发
                self.PutEvent.succeed()
                # 放置事件复位
                self.PutEvent = self.env.event()

    def GantryMove(self):
        while True:
            # 抛出大车移动就绪事件，等待事件触发
            yield self.GantryMoveEvent
            # 大车移动就绪事件复位
            self.GantryMoveEvent = self.env.event()
            while True:
                self.GantryMoveingFlag = True
                if self.CurrentPos[0] == self.TargetPos[0]:
                    break
                else:
                    if self.CurrentPos[0] < self.TargetPos[0]:
                        # 获取轨道吊当前位置
                        CranePos = Crane.CurrentPos
                        # 判断安全距离内是否有轨道吊，若是则原地等待，否则移动一个贝位
                        while True:
                            if CranePos[0] == self.CurrentPos[
                                0] + self.Safe_Distance and self.Worklinepos == WorkLinePos.Down:
                                yield self.env.timeout(1)
                            else:
                                break
                        # 移动
                        try:
                            yield self.env.timeout(self.GANTRY_SPEED)
                        except simpy.Interrupt:
                            self.InterruptFlag = True
                            self.GantryMoveProcessInterruptOver.succeed()
                            break
                        self.CurrentPos[0] += 1
                        print("正面吊大车移动到贝位{}".format(self.CurrentPos[0]))
                        print("当前时间为{}".format(self.env.now))
                    else:
                        # 获取轨道吊当前位置
                        CranePos = Crane.CurrentPos
                        # 判断安全距离内是否有轨道吊，若是则原地等待，否则移动一个贝位
                        while True:
                            if CranePos[0] == self.CurrentPos[
                                0] - self.Safe_Distance and self.Worklinepos == WorkLinePos.Down:
                                yield self.env.timeout(1)
                            else:
                                break
                        try:
                            yield self.env.timeout(self.GANTRY_SPEED)
                        except simpy.Interrupt:
                            self.InterruptFlag = True
                            self.GantryMoveProcessInterruptOver.succeed()
                            break
                        self.CurrentPos[0] -= 1
                        print("正面吊大车移动到贝位{}".format(self.CurrentPos[0]))
                        print("当前时间为{}".format(self.env.now))
            self.GantryMoveingFlag = False
            self.GantryMoveOverEvent.succeed()
            self.GantryMoveOverEvent = self.env.event()

    def TrolleyMove(self):
        while True:
            # 抛出小车移动就绪事件，等待事件触发
            yield self.TrolleyMoveEvent
            self.TrolleyMoveEvent = self.env.event()
            while True:
                self.TrolleyMoveingFlag = True
                if self.CurrentPos[1] == self.TargetPos[1]:
                    break
                else:
                    if self.CurrentPos[1] < self.TargetPos[1]:
                        try:
                            yield self.env.timeout(self.TROLLEY_SPEED)
                        except simpy.Interrupt:
                            self.InterruptFlag = True
                            self.TrolleyMoveProcessInterruptOver.succeed()
                            break
                        self.CurrentPos[1] += 1
                        if self.CurrentPos[1] in [13,14]:
                            self.Worklinepos = WorkLinePos.Up
                        else:
                            self.Worklinepos = WorkLinePos.Down
                        print("正面吊小车移动到栈位{}".format(self.CurrentPos[1]))
                        print("当前时间为{}".format(self.env.now))
                    else:
                        try:
                            yield self.env.timeout(self.TROLLEY_SPEED)
                        except simpy.Interrupt:
                            self.InterruptFlag = True
                            self.TrolleyMoveProcessInterruptOver.succeed()
                            break
                        self.CurrentPos[1] -= 1
                        if self.CurrentPos[1] in [13,14]:
                            self.Worklinepos = WorkLinePos.Up
                        else:
                            self.Worklinepos = WorkLinePos.Down
                        print("正面吊小车移动到栈位{}".format(self.CurrentPos[1]))
                        print("当前时间为{}".format(self.env.now))
            self.TrolleyMoveingFlag = False
            self.TrolleyMoveOverEvent.succeed()
            self.TrolleyMoveOverEvent = self.env.event()

    def Pick(self):
        while True:
            # 抛出抓取作业事件，等待事件触发
            yield self.PickEvent
            # 抓取作业事件触发
            PickUpContainerLocation = ContainerLocation.FindContainerLocation(self.CurrentTask['StartPosition'][0],
                                                                              self.CurrentTask['StartPosition'][1],
                                                                              self.CurrentTask['StartPosition'][
                                                                                  2]).pop()
            # 进入抓取集装箱状态
            self.StateCode = 2
            # 模拟抓取时间
            yield self.env.timeout(self.PICK_TIME)
            # 进入运输集装箱状态
            self.LoadOn = True
            # 集装箱箱位状态改变
            PickUpContainerLocation.state = 0
            HTArray = np.zeros((2, 14, 66))
            for object_ in ContainerLocation.ContainerList:
                HTArray[object_.level - 1, object_.stack - 1, object_.bay - 1] = object_.state
            DrawPicture.ContainerChangeLocationList.append(copy.deepcopy(HTArray))
            DrawPicture.ContainerChangeLocationTimeList.append(env.now)
            self.StateCode = 3
            # 返回通道入口
            self.TargetPos = self.EnterPos
            self.GantryMoveEvent.succeed()
            self.GantryMoveEvent = self.env.event()
            yield self.GantryMoveOverEvent
            self.TrolleyMoveEvent.succeed()
            self.TrolleyMoveEvent = self.env.event()
            yield self.TrolleyMoveOverEvent
            # 重置进入通道位置
            self.EnterPos = []
            # 进入等待调度状态
            self.StateCode = 0
            while True:
                try:
                    ShaduleEvent.succeed(self)
                    break
                except:
                    yield env.timeout(1)

    def Put(self):
        while True:
            yield self.PutEvent
            PutContainerLocation = ContainerLocation.FindContainerLocation(self.CurrentTask['EndPosition'][0],
                                                                           self.CurrentTask['EndPosition'][1],
                                                                           self.CurrentTask['EndPosition'][2]).pop()
            # 进入放置集装箱状态
            self.StateCode = 4
            # 模拟放置时间
            yield self.env.timeout(self.PUT_TIME)
            # 放置集装箱
            self.LoadOn = False
            # 集装箱箱位状态改变
            PutContainerLocation.state = 1
            HTArray = np.zeros((2, 14, 66))
            for object_ in ContainerLocation.ContainerList:
                HTArray[object_.level - 1, object_.stack - 1, object_.bay - 1] = object_.state
            DrawPicture.ContainerChangeLocationList.append(copy.deepcopy(HTArray))
            DrawPicture.ContainerChangeLocationTimeList.append(env.now)
            self.StateCode = 1
            # 返回通道入口
            self.TargetPos = self.EnterPos
            self.GantryMoveEvent.succeed()
            self.GantryMoveEvent = self.env.event()
            yield self.GantryMoveOverEvent
            self.TrolleyMoveEvent.succeed()
            self.TrolleyMoveEvent = self.env.event()
            yield self.TrolleyMoveOverEvent
            self.EnterPos = []
            # 进入空闲状态
            self.StateCode = -2
            # 任务完成，清空任务
            self.CurrentTask.clear()
            self.TargetPos = self.CurrentPos
            ReachStackerTaskQueue.pop(0)
            # 下一次调度
            while True:
                try:
                    ShaduleEvent.succeed(self)
                    break
                except:
                    yield env.timeout(1)

    def Wait(self):
        while True:
            yield self.WaitEvent
            # 重置等待事件
            self.WaitEvent = self.env.event()
            if self.StateCode == -1:
                self.StateCode = -1
            else:
                self.StateCode = 0
            # 进入等待状态
            yield self.env.timeout(5)
            # 等待调度
            while True:
                try:
                    ShaduleEvent.succeed(self)
                    break
                except:
                    yield env.timeout(1)
            self.DeviceLock = False


"----------------------------轨道吊类-----------------------------"


class Crane(simpy.Resource):
    CraneName = dict()

    def __init__(self, env, name):
        super().__init__(env)
        self.name = name
        self.env = env
        self.CurrentTask = {}  # 记录轨道吊当前任务
        self.CurrentPos = [18, 0]  # 记录轨道吊当前位置
        self.AvoidancePos = []  # 记录轨道吊避让位置
        self.StateCode = -2
        self.GANTRY_SPEED = 8  # 移动一个贝位所需要的时间
        self.TROLLEY_SPEED = 6  # 移动一个栈位所需要的时间
        self.PICK_TIME = 50  # 抓取集装箱的所需时间
        self.PUT_TIME = 50  # 放置集装箱的所需时间
        self.Safe_Distance = SafetyDistance  # 安全距离
        self.TargetPos = [18, 0]  # 移动目标位置
        self.PosRegister = []  # 记录轨道吊大车位置
        Crane.CraneName[self.name] = self

        # 标志位定义
        self.GantryMoveingFlag = False  # 大车正在移动标志位
        self.TrolleyMoveingFlag = False  # 小车正在移动标志位
        self.InterruptFlag = False  # 中断标志位
        self.DeviceLock = False  # 设备锁
        self.LoadOn = False  # 负载状态

        # 事件定义
        self.GantryMoveEvent = self.env.event()  # 大车开始移动事件
        self.GantryMoveOverEvent = self.env.event()  # 大车移动结束事件
        self.TrolleyMoveEvent = self.env.event()  # 小车开始移动事件
        self.TrolleyMoveOverEvent = self.env.event()  # 小车移动结束事件
        self.MoveEvent = self.env.event()  # 移动事件
        self.PickEvent = self.env.event()  # 抓取事件
        self.PutEvent = self.env.event()  # 放置事件
        self.AvoidanceEvent = self.env.event()  # 避让事件
        self.AvoidanceOverEvent = self.env.event()  # 避让完成事件
        self.WaitEvent = self.env.event()  # 等待事件
        self.InterruptWorkEvent = self.env.event()
        self.GantryMoveProcessInterruptOver = self.env.event()
        self.TrolleyMoveProcessInterruptOver = self.env.event()

        # 进程定义
        self.GantryMoveProcess = self.env.process(self.GantryMove())
        self.TrolleyMoveProcess = self.env.process(self.TrolleyMove())
        self.env.process(self.Move())
        self.env.process(self.Pick())
        self.env.process(self.Put())
        self.env.process(self.AvoidanceMove())
        self.env.process(self.Wait())
        self.env.process(self.LocationRecord())
        self.env.process(self.InterruptWork())

    def LocationRecord(self):
        "记录轨道吊位置"
        while True:
            yield self.env.timeout(1)
            self.PosRegister.append([self.CurrentPos[0], self.CurrentPos[1], self.StateCode, self.env.now])

    @classmethod
    def GetInfo(cls, name):
        reachtSacker = cls.CraneName[name]
        infodict = dict()
        infodict['StateCode'] = reachtSacker.StateCode
        infodict['CurrentPos'] = reachtSacker.CurrentPos
        infodict['CurrentTask'] = reachtSacker.CurrentTask
        infodict['TargetPos'] = reachtSacker.TargetPos
        return infodict

    def Move(self):
        while True:
            yield self.MoveEvent
            self.MoveEvent = self.env.event()
            if self.StateCode == 0 and self.LoadOn == False:
                self.TargetPos = self.CurrentTask['StartPosition']
                self.StateCode = 1
                self.GantryMoveEvent.succeed()
                self.TrolleyMoveEvent.succeed()
                self.GantryMoveEvent = self.env.event()
                self.TrolleyMoveEvent = self.env.event()
                yield self.GantryMoveOverEvent & self.TrolleyMoveOverEvent
                if self.InterruptFlag:
                    self.InterruptFlag = False
                    self.MoveEvent = self.env.event()
                    self.StateCode = 0
                    continue
                self.PickEvent.succeed()

            if self.StateCode == 0 and self.LoadOn == True:
                self.TargetPos = self.CurrentTask['EndPosition']
                self.StateCode = 3
                self.GantryMoveEvent.succeed()
                self.TrolleyMoveEvent.succeed()
                self.GantryMoveEvent = self.env.event()
                self.TrolleyMoveEvent = self.env.event()
                yield self.GantryMoveOverEvent & self.TrolleyMoveOverEvent
                if self.InterruptFlag:
                    self.InterruptFlag = False
                    self.MoveEvent = self.env.event()
                    self.StateCode = 0
                    continue
                self.PutEvent.succeed()

    def AvoidanceMove(self):
        while True:
            yield self.AvoidanceEvent
            self.AvoidanceEvent = self.env.event()
            # 将作业状态设为-1，表示避让
            self.StateCode = -1
            self.TargetPos = self.AvoidancePos
            self.GantryMoveEvent.succeed()
            self.TrolleyMoveEvent.succeed()
            self.GantryMoveEvent = self.env.event()
            self.TrolleyMoveEvent = self.env.event()
            yield self.GantryMoveOverEvent & self.TrolleyMoveOverEvent
            self.InterruptFlag = False
            # 将作业状态设为有任务空闲状态
            self.StateCode = 0
            # 避让完成,进行下次调度
            while True:
                try:
                    ShaduleEvent.succeed(self)
                    break
                except:
                    yield env.timeout(1)
            self.DeviceLock = False

    def GantryMove(self):
        while True:
            yield self.GantryMoveEvent
            self.GantryMoveEvent = self.env.event()
            while True:
                self.GantryMoveingFlag = True
                if self.CurrentPos[0] == self.TargetPos[0]:
                    break
                else:
                    if self.CurrentPos[0] < self.TargetPos[0]:
                        ReachStackerPos = ReachStacker.CurrentPos
                        while True:
                            if ReachStackerPos[0] == self.CurrentPos[
                                0] + self.Safe_Distance and ReachStacker.Worklinepos == WorkLinePos.Down:
                                yield self.env.timeout(1)
                            else:
                                break
                        try:
                            yield self.env.timeout(self.GANTRY_SPEED)
                        except simpy.Interrupt:
                            self.InterruptFlag = True
                            self.GantryMoveProcessInterruptOver.succeed()
                            break
                        self.CurrentPos[0] += 1
                        print("轨道吊大车移动到贝位{}".format(self.CurrentPos[0]))
                        print("当前时间为{}".format(self.env.now))
                    else:
                        ReachStackerPos = ReachStacker.CurrentPos
                        while True:
                            if ReachStackerPos[0] == self.CurrentPos[
                                0] - self.Safe_Distance and ReachStacker.Worklinepos == WorkLinePos.Down:
                                yield self.env.timeout(1)
                            else:
                                break
                        try:
                            yield self.env.timeout(self.GANTRY_SPEED)
                        except simpy.Interrupt:
                            self.InterruptFlag = True
                            self.GantryMoveProcessInterruptOver.succeed()
                            break
                        self.CurrentPos[0] -= 1
                        print("轨道吊大车移动到贝位{}".format(self.CurrentPos[0]))
                        print("当前时间为{}".format(self.env.now))
            self.GantryMoveingFlag = False
            self.GantryMoveOverEvent.succeed()
            self.GantryMoveOverEvent = self.env.event()

    def TrolleyMove(self):
        while True:
            yield self.TrolleyMoveEvent
            self.TrolleyMoveEvent = self.env.event()
            while True:
                self.TrolleyMoveingFlag = True
                if self.CurrentPos[1] == self.TargetPos[1]:
                    break
                else:
                    if self.CurrentPos[1] < self.TargetPos[1]:
                        try:
                            yield self.env.timeout(self.TROLLEY_SPEED)
                        except simpy.Interrupt:
                            self.InterruptFlag = True
                            self.TrolleyMoveProcessInterruptOver.succeed()
                            break
                        self.CurrentPos[1] += 1
                        print("轨道吊小车移动到栈位{}".format(self.CurrentPos[1]))
                        print("当前时间为{}".format(self.env.now))
                    else:
                        try:
                            yield self.env.timeout(self.TROLLEY_SPEED)
                        except simpy.Interrupt:
                            self.InterruptFlag = True
                            self.TrolleyMoveProcessInterruptOver.succeed()
                            break
                        self.CurrentPos[1] -= 1
                        print("轨道吊小车移动到栈位{}".format(self.CurrentPos[1]))
                        print("当前时间为{}".format(self.env.now))
            self.TrolleyMoveingFlag = False
            self.TrolleyMoveOverEvent.succeed()
            self.TrolleyMoveOverEvent = self.env.event()

    def Pick(self):
        while True:
            yield self.PickEvent
            self.PickEvent = self.env.event()
            # 进入抓取集装箱状态
            self.StateCode = 2
            # 模拟抓取时间
            yield self.env.timeout(self.PICK_TIME)
            self.LoadOn = True
            # 箱位状态调整
            PickContainerLocation = ContainerLocation.FindContainerLocation(self.CurrentTask['StartPosition'][0],
                                                                            self.CurrentTask['StartPosition'][1],
                                                                            self.CurrentTask['StartPosition'][2]).pop()
            PickContainerLocation.state = 0
            HTArray = np.zeros((2, 14, 66))
            for object_ in ContainerLocation.ContainerList:
                HTArray[object_.level - 1, object_.stack - 1, object_.bay - 1] = object_.state
            DrawPicture.ContainerChangeLocationList.append(copy.deepcopy(HTArray))
            DrawPicture.ContainerChangeLocationTimeList.append(env.now)
            self.StateCode = 0
            while True:
                try:
                    ShaduleEvent.succeed(self)
                    break
                except:
                    yield env.timeout(1)

    def Put(self):
        while True:
            yield self.PutEvent
            self.PutEvent = self.env.event()
            # 进入放置集装箱状态
            self.StateCode = 4
            # 模拟放置时间
            yield self.env.timeout(self.PUT_TIME)
            self.LoadOn = False
            PutContainerLocation = ContainerLocation.FindContainerLocation(self.CurrentTask['EndPosition'][0],
                                                                           self.CurrentTask['EndPosition'][1],
                                                                           self.CurrentTask['EndPosition'][2]).pop()
            PutContainerLocation.state = 1
            HTArray = np.zeros((2, 14, 66))
            for object_ in ContainerLocation.ContainerList:
                HTArray[object_.level - 1, object_.stack - 1, object_.bay - 1] = object_.state
            DrawPicture.ContainerChangeLocationList.append(copy.deepcopy(HTArray))
            DrawPicture.ContainerChangeLocationTimeList.append(env.now)
            # 进入空闲状态
            self.StateCode = -2
            self.PutEvent = self.env.event()
            # 任务完成，清空任务
            self.CurrentTask.clear()
            self.TargetPos = self.CurrentPos
            CraneTaskQueue.pop(0)
            # 下一次调度
            while True:
                try:
                    ShaduleEvent.succeed(self)
                    break
                except:
                    yield env.timeout(1)

    def Wait(self):
        while True:
            yield self.WaitEvent
            self.WaitEvent = self.env.event()
            if self.StateCode == -1:
                self.StateCode = -1
            else:
                self.StateCode = 0
            # 进入等待状态
            yield self.env.timeout(5)
            # 重置等待事件
            # 等待调度
            while True:
                try:
                    ShaduleEvent.succeed(self)
                    break
                except:
                    yield env.timeout(1)
            self.DeviceLock = False

    def InterruptWork(self):
        while True:
            yield self.InterruptWorkEvent
            if self.GantryMoveingFlag:
                self.GantryMoveProcess.interrupt()
                yield self.GantryMoveProcessInterruptOver
                self.GantryMoveProcessInterruptOver = self.env.event()
            if self.TrolleyMoveingFlag:
                self.TrolleyMoveProcess.interrupt()
                yield self.TrolleyMoveProcessInterruptOver
                self.TrolleyMoveProcessInterruptOver = self.env.event()


"----------------------------主函数-----------------------------"
if __name__ == "__main__":
    DP = DrawPicture()
    ContainerLocationTable = pd.read_excel("箱位状态表(20240325目前可用).xlsx")
    for index, row in ContainerLocationTable.iterrows():
        ContainerLocation(stack=row["栈位"], bay=row["贝位"], level=row["层位"], state=row["占用状态"],
                          Locationtype=row["位置类型"])
    HTArray = np.zeros((2, 14, 66))
    for object_ in ContainerLocation.ContainerList:
        HTArray[object_.level - 1, object_.stack - 1, object_.bay - 1] = object_.state
    InitHTArray = copy.deepcopy(HTArray)
    HTArray = np.zeros((2, 14, 66))
    ReachStacker = ReachStacker(env, "ReachStacker")
    Crane = Crane(env, "Crane")
    ControllStation = ControllStation(env)
    env.process(CheckDeviceDeadLock(ReachStacker, Crane))
    # 计算程序运行时间
    start = time.time()
    try:
        env.run(SimulationStopEvent)
    except Exception as e:
        print(e)
    end = time.time()
    DP.LoadData(Crane.PosRegister, ReachStacker.PosRegister, InitHTArray)
    print("程序运行时间为{}".format(end - start))
    plt.show()
    print(Crane.PosRegister)
    print(ReachStacker.PosRegister)
